home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / TURBOPASCAL WIN / OWLDEMOS.PAK / OLECLNT.PAS < prev    next >
Pascal/Delphi Source File  |  1992-06-08  |  36KB  |  1,245 lines

  1.  
  2. {**************************************************}
  3. {                                                  }
  4. {   Turbo Pascal for Windows                       }
  5. {   Object Linking and Embedding demo program      }
  6. {                                                  }
  7. {   Copyright (c) 1992 by Borland International    }
  8. {                                                  }
  9. {**************************************************}
  10.  
  11. program OleClnt;
  12.  
  13. { This program demonstrates how to implement an OLE client application.
  14.   The program uses the new Ole, ShellAPI, and CommDlg units, and requires
  15.   that the OLECLI.DLL, SHELL.DLL, and COMMDLG.DLL libraries are present.
  16.   The program allows you to create embedded and linked objects using the
  17.   Edit|Paste and Edit|Paste link commands. The OLE objects can be moved
  18.   and resized, and they can be activated through double clicks or using
  19.   the Edit|Object menu. Workspaces can be saved and loaded using the
  20.   File menu. }
  21.  
  22. uses Strings, WinTypes, WinProcs, WObjects, Ole, ShellAPI, CommDlg;
  23.  
  24. {$R OLECLNT}
  25.  
  26. const
  27.  
  28. { Resource IDs }
  29.  
  30.   id_Menu  = 100;
  31.   id_About = 100;
  32.  
  33. { Menu command IDs }
  34.  
  35.   cm_FileNew       = 100;
  36.   cm_FileOpen      = 101;
  37.   cm_FileSave      = 102;
  38.   cm_FileSaveAs    = 103;
  39.   cm_FileExit      = 104;
  40.   cm_EditCut       = 200;
  41.   cm_EditCopy      = 201;
  42.   cm_EditPaste     = 202;
  43.   cm_EditPasteLink = 203;
  44.   cm_EditClear     = 204;
  45.   cm_HelpAbout     = 300;
  46.   cm_VerbMin       = 900;
  47.   cm_VerbMax       = 999;
  48.  
  49. { Menu item positions }
  50.  
  51.   pos_Edit   = 1;  { Position of Edit item on main menu }
  52.   pos_Object = 6;  { Position of Object item on Edit menu }
  53.  
  54. type
  55.  
  56. { Pointer types }
  57.  
  58.   PAppClient    = ^TAppClient;
  59.   PAppStream    = ^TAppStream;
  60.   PObjectWindow = ^TObjectWindow;
  61.   PMainWindow   = ^TMainWindow;
  62.  
  63. { Filename string }
  64.  
  65.   TFilename = array[0..255] of Char;
  66.  
  67. { OLE file header }
  68.  
  69.   TOleFileHeader = array[1..4] of Char;
  70.  
  71. { Application client structure }
  72.  
  73.   TAppClient = record
  74.     OleClient: TOleClient;
  75.     ObjectWindow: PObjectWindow;
  76.   end;
  77.  
  78. { Application stream structure }
  79.  
  80.   TAppStream = record
  81.     OleStream: TOleStream;
  82.     OwlStream: PStream;
  83.   end;
  84.  
  85. { OLE object window }
  86.  
  87.   TObjectWindow = object(TWindow)
  88.     AppClient: TAppClient;
  89.     OleObject: POleObject;
  90.     Framed: Boolean;
  91.     constructor Init(Link: Boolean);
  92.     constructor Load(var S: TStream);
  93.     destructor Done; virtual;
  94.     function GetClassName: PChar; virtual;
  95.     procedure GetWindowClass(var AWndClass: TWndClass); virtual;
  96.     procedure SetupWindow; virtual;
  97.     procedure Store(var S: TStream); virtual;
  98.     function CanClose: Boolean; virtual;
  99.     procedure Paint(PaintDC: HDC; var PaintInfo: TPaintStruct); virtual;
  100.     procedure Check(OleStatus: TOleStatus);
  101.     procedure GetObjectClass(ClassName: PChar);
  102.     function IsLinked: Boolean;
  103.     procedure Update;
  104.     procedure OpenObject(Verb: Word);
  105.     procedure CloseObject;
  106.     procedure CopyToClipboard;
  107.     procedure Delete;
  108.     procedure Changed;
  109.     procedure BringToFront;
  110.     procedure GetBounds(var R: TRect);
  111.     procedure SetBounds(var R: TRect);
  112.     procedure ShowFrame(EnableFrame: Boolean);
  113.     procedure WMGetMinMaxInfo(var Msg: TMessage);
  114.       virtual wm_First + wm_GetMinMaxInfo;
  115.     procedure WMMove(var Msg: TMessage);
  116.       virtual wm_First + wm_Move;
  117.     procedure WMSize(var Msg: TMessage);
  118.       virtual wm_First + wm_Size;
  119.     procedure WMLButtonDown(var Msg: TMessage);
  120.       virtual wm_First + wm_LButtonDown;
  121.     procedure WMMouseMove(var Msg: TMessage);
  122.       virtual wm_First + wm_MouseMove;
  123.     procedure WMLButtonUp(var Msg: TMessage);
  124.       virtual wm_First + wm_LButtonUp;
  125.     procedure WMLButtonDblClk(var Msg: TMessage);
  126.       virtual wm_First + wm_LButtonDblClk;
  127.   end;
  128.  
  129. { Application main window }
  130.  
  131.   TMainWindow = object(TWindow)
  132.     ObjectWindow: PObjectWindow;
  133.     ClientDoc: LHClientDoc;
  134.     Modified: Boolean;
  135.     Filename: TFilename;
  136.     constructor Init;
  137.     destructor Done; virtual;
  138.     function CanClose: Boolean; virtual;
  139.     procedure InitDocument;
  140.     procedure DoneDocument;
  141.     procedure UpdateDocument;
  142.     procedure SetFilename(Name: PChar);
  143.     function NewFile(Name: PChar): Boolean;
  144.     function LoadFile: Boolean;
  145.     function SaveFile: Boolean;
  146.     function Save: Boolean;
  147.     function SaveAs: Boolean;
  148.     procedure NewObjectWindow(Link: Boolean);
  149.     procedure SelectWindow(Window: PObjectWindow);
  150.     procedure UpdateObjectMenu;
  151.     procedure WMLButtonDown(var Msg: TMessage);
  152.       virtual wm_First + wm_LButtonDown;
  153.     procedure WMInitMenu(var Msg: TMessage);
  154.       virtual wm_First + wm_InitMenu;
  155.     procedure CMFileNew(var Msg: TMessage);
  156.       virtual cm_First + cm_FileNew;
  157.     procedure CMFileOpen(var Msg: TMessage);
  158.       virtual cm_First + cm_FileOpen;
  159.     procedure CMFileSave(var Msg: TMessage);
  160.       virtual cm_First + cm_FileSave;
  161.     procedure CMFileSaveAs(var Msg: TMessage);
  162.       virtual cm_First + cm_FileSaveAs;
  163.     procedure CMFileExit(var Msg: TMessage);
  164.       virtual cm_First + cm_FileExit;
  165.     procedure CMEditCut(var Msg: TMessage);
  166.       virtual cm_First + cm_EditCut;
  167.     procedure CMEditCopy(var Msg: TMessage);
  168.       virtual cm_First + cm_EditCopy;
  169.     procedure CMEditPaste(var Msg: TMessage);
  170.       virtual cm_First + cm_EditPaste;
  171.     procedure CMEditPasteLink(var Msg: TMessage);
  172.       virtual cm_First + cm_EditPasteLink;
  173.     procedure CMEditClear(var Msg: TMessage);
  174.       virtual cm_First + cm_EditClear;
  175.     procedure CMHelpAbout(var Msg: TMessage);
  176.       virtual cm_First + cm_HelpAbout;
  177.     procedure DefCommandProc(var Msg: TMessage); virtual;
  178.   end;
  179.  
  180. { Application object }
  181.  
  182.   TApp = object(TApplication)
  183.     constructor Init(AName: PChar);
  184.     destructor Done; virtual;
  185.     procedure InitMainWindow; virtual;
  186.   end;
  187.  
  188. { Initialized globals }
  189.  
  190. const
  191.   Dragging: Boolean = False;
  192.   OleFileHeader: TOleFileHeader = 'TPOF';
  193.   OleProtocol: PChar = 'StdFileEditing';
  194.   OleObjectName: PChar = 'Object';
  195.   OleClntTitle: PChar = 'OLE Client Demo';
  196.  
  197. { Global variables }
  198.  
  199. var
  200.   App: TApp;
  201.   DragPoint: TPoint;
  202.   MainWindow: PMainWindow;
  203.   OleClientVTbl: TOleClientVTbl;
  204.   OleStreamVTbl: TOleStreamVTbl;
  205.   PixPerInch: TPoint;
  206.   CFObjectLink, CFOwnerLink: Word;
  207.  
  208. { TObjectWindow stream registration record }
  209.  
  210. const
  211.   RObjectWindow: TStreamRec = (
  212.     ObjType: 999;
  213.     VmtLink: Ofs(TypeOf(TObjectWindow)^);
  214.     Load: @TObjectWindow.Load;
  215.     Store: @TObjectWindow.Store);
  216.  
  217. { Display a message using the MessageBox API routine. }
  218.  
  219. function Message(S: PChar; Flags: Word): Word;
  220. begin
  221.   Message := MessageBox(MainWindow^.HWindow, S, OleClntTitle, Flags);
  222. end;
  223.  
  224. { Display an error message. }
  225.  
  226. procedure Error(ErrorStr, ErrorArg: PChar);
  227. var
  228.   S: array[0..255] of Char;
  229. begin
  230.   wvsprintf(S, ErrorStr, ErrorArg);
  231.   Message(S, mb_IconExclamation + mb_Ok);
  232. end;
  233.  
  234. { Display OLE operation error message. }
  235.  
  236. procedure OleError(Status: Word);
  237. var
  238.   S: array[0..7] of Char;
  239. begin
  240.   wvsprintf(S, '%d', Status);
  241.   Error('Warning: OLE operation failed, error code = %s.', S);
  242. end;
  243.  
  244. { Display an Open or Save As file dialog using the Common Dialog DLL. }
  245.  
  246. function FileDialog(Owner: HWnd; Filename: PChar; Save: Boolean): Boolean;
  247. const
  248.   DefOpenFilename: TOpenFilename = (
  249.     lStructSize: SizeOf(TOpenFilename);
  250.     hwndOwner: 0;
  251.     hInstance: 0;
  252.     lpstrFilter: 'OLE files (*.OLE)'#0'*.ole'#0;
  253.     lpstrCustomFilter: nil;
  254.     nMaxCustFilter: 0;
  255.     nFilterIndex: 0;
  256.     lpstrFile: nil;
  257.     nMaxFile: SizeOf(TFilename);
  258.     lpstrFileTitle: nil;
  259.     nMaxFileTitle: 0;
  260.     lpstrInitialDir: nil;
  261.     lpstrTitle: nil;
  262.     Flags: 0;
  263.     nFileOffset: 0;
  264.     nFileExtension: 0;
  265.     lpstrDefExt: 'ole');
  266. var
  267.   OpenFilename: TOpenFilename;
  268. begin
  269.   OpenFilename := DefOpenFilename;
  270.   OpenFilename.hwndOwner := Owner;
  271.   OpenFilename.lpstrFile := Filename;
  272.   if Save then
  273.   begin
  274.     OpenFilename.Flags := ofn_PathMustExist + ofn_NoChangeDir +
  275.       ofn_OverwritePrompt;
  276.     FileDialog := GetSaveFilename(OpenFilename);
  277.   end else
  278.   begin
  279.     OpenFileName.Flags := ofn_PathMustExist + ofn_HideReadOnly;
  280.     FileDialog := GetOpenFilename(OpenFilename);
  281.   end;
  282. end;
  283.  
  284. { OLE client callback routine. Called by the OLE client library to notify
  285.   the application of any changes to an object. In this application, the
  286.   Client parameter is always a PAppClient, so a typecast can be used to
  287.   find the corresponding TObjectWindow. The OLE object window's Changed
  288.   method is called whenever the contained OLE object is changed, saved,
  289.   or renamed. The callback routine returns 1 to satisfy ole_Query_Paint
  290.   and ole_Query_Retry notifications. }
  291.  
  292. function ClientCallBack(Client: POleClient; Notification:
  293.   TOle_Notification; OleObject: POleObject): Integer; export;
  294. begin
  295.   ClientCallBack := 1;
  296.   case Notification of
  297.     ole_Changed, ole_Saved, ole_Renamed:
  298.       PAppClient(Client)^.ObjectWindow^.Changed;
  299.   end;
  300. end;
  301.  
  302. { Selector increment. This is not a true procedure. Instead, it is an
  303.   external symbol whose offset represents the value to add to a selector
  304.   to increment a pointer by 64K bytes. }
  305.  
  306. procedure AHIncr; far; external 'KERNEL' index 114;
  307.  
  308. { Read or write to or from a stream. This function supports transfers of
  309.   blocks larger than 64K bytes. It guards against segment overruns, and
  310.   transfers data in blocks of up to 32K bytes. }
  311.  
  312. function StreamInOut(var S: TStream; Buffer: Pointer; Size: Longint;
  313.   Writing: Boolean): Longint;
  314. var
  315.   N: Longint;
  316. begin
  317.   StreamInOut := Size;
  318.   while Size <> 0 do
  319.   begin
  320.     N := $10000 - PtrRec(Buffer).Ofs;
  321.     if N > $8000 then N := $8000;
  322.     if N > Size then N := Size;
  323.     if Writing then S.Write(Buffer^, N) else S.Read(Buffer^, N);
  324.     Inc(PtrRec(Buffer).Ofs, N);
  325.     if PtrRec(Buffer).Ofs = 0 then Inc(PtrRec(Buffer).Seg, Ofs(AHIncr));
  326.     Dec(Size, N);
  327.   end;
  328.   if S.Status <> 0 then StreamInOut := 0;
  329. end;
  330.  
  331. { OLE stream read callback function. In this application, the Stream
  332.   parameter is always a PAppStream, so a typecast can be used to find the
  333.   corresponding ObjectWindows stream. }
  334.  
  335. function StreamGet(Stream: POleStream; Buffer: PChar;
  336.   Size: LongInt): LongInt; export;
  337. begin
  338.   StreamGet := StreamInOut(PAppStream(Stream)^.OwlStream^,
  339.     Buffer, Size, False);
  340. end;
  341.  
  342. { OLE stream write callback function. In this application, the Stream
  343.   parameter is always a PAppStream, so a typecast can be used to find the
  344.   corresponding ObjectWindows stream. }
  345.  
  346. function StreamPut(Stream: POleStream; Buffer: PChar;
  347.   Size: LongInt): LongInt; export;
  348. begin
  349.   StreamPut := StreamInOut(PAppStream(Stream)^.OwlStream^,
  350.     Buffer, Size, True);
  351. end;
  352.  
  353. { TObjectWindow methods }
  354.  
  355. { Construct an OLE object window. The AppClient structure is initialized
  356.   to reference the newly created TObjectWindow so that the ClientCallBack
  357.   routine can later locate it when notifications are received. If the OLE
  358.   object is successfully created, its bounds are queried to determine the
  359.   initial bounds of the OLE object window. Notice that the bounds are
  360.   returned in mm_HiMetric units, which are converted to mm_Text units. }
  361.  
  362. constructor TObjectWindow.Init(Link: Boolean);
  363. var
  364.   R: TRect;
  365. begin
  366.   TWindow.Init(MainWindow, nil);
  367.   Attr.Style := ws_Child + ws_ClipSiblings;
  368.   AppClient.OleClient.lpvtbl := @OleClientVTbl;
  369.   AppClient.ObjectWindow := @Self;
  370.   OleObject := nil;
  371.   Framed := False;
  372.   if Link then
  373.     Check(OleCreateLinkFromClip(OleProtocol, @AppClient.OleClient,
  374.       MainWindow^.ClientDoc, OleObjectName, OleObject,
  375.       olerender_Draw, 0))
  376.   else
  377.     Check(OleCreateFromClip(OleProtocol, @AppClient.OleClient,
  378.       MainWindow^.ClientDoc, OleObjectName, OleObject,
  379.       olerender_Draw, 0));
  380.   if OleObject = nil then Status := -1 else
  381.   begin
  382.     OleQueryBounds(OleObject, R);
  383.     Attr.X := 0;
  384.     Attr.Y := 0;
  385.     Attr.W := MulDiv(R.right, PixPerInch.X, 2540);
  386.     Attr.H := MulDiv(-R.bottom, PixPerInch.Y, 2540);
  387.   end;
  388. end;
  389.  
  390. { Load an OLE object window from a stream. Loads the contained OLE object
  391.   from the stream, using a TAppStream for I/O. }
  392.  
  393. constructor TObjectWindow.Load(var S: TStream);
  394. var
  395.   ObjectType: Longint;
  396.   AppStream: TAppStream;
  397. begin
  398.   TWindow.Load(S);
  399.   AppClient.OleClient.lpvtbl := @OleClientVTbl;
  400.   AppClient.ObjectWindow := @Self;
  401.   OleObject := nil;
  402.   Framed := False;
  403.   AppStream.OleStream.lpstbl := @OleStreamVTbl;
  404.   AppStream.OwlStream := @S;
  405.   Check(OleLoadFromStream(@AppStream.OleStream, OleProtocol,
  406.     @AppClient.OleClient, MainWindow^.ClientDoc, OleObjectName,
  407.     OleObject));
  408.   if OleObject = nil then Status := -1;
  409. end;
  410.  
  411. { Destroy an OLE object window. Closes and releases the contained OLE
  412.   object. }
  413.  
  414. destructor TObjectWindow.Done;
  415. begin
  416.   if OleObject <> nil then
  417.   begin
  418.     CloseObject;
  419.     Check(OleRelease(OleObject));
  420.   end;
  421.   TWindow.Done;
  422. end;
  423.  
  424. { Return the OLE object window class name }
  425.  
  426. function TObjectWindow.GetClassName: PChar;
  427. begin
  428.   GetClassName := 'OleWindow';
  429. end;
  430.  
  431. { Return the OLE object window class structure. Enables double click
  432.   processing. }
  433.  
  434. procedure TObjectWindow.GetWindowClass(var AWndClass: TWndClass);
  435. begin
  436.   TWindow.GetWindowClass(AWndClass);
  437.   AWndClass.Style := AWndClass.Style or cs_DblClks;
  438. end;
  439.  
  440. { Initialize an OLE object window. Called following successful creation
  441.   of the MS-Windows window. The window is brought to front and shown. }
  442.  
  443. procedure TObjectWindow.SetupWindow;
  444. begin
  445.   TWindow.SetupWindow;
  446.   BringToFront;
  447.   ShowWindow(HWindow, sw_Show);
  448. end;
  449.  
  450. { Store an OLE object window on a stream. Stores the contained OLE object
  451.   on the stream, using a TAppStream for I/O. }
  452.  
  453. procedure TObjectWindow.Store(var S: TStream);
  454. var
  455.   AppStream: TAppStream;
  456. begin
  457.   TWindow.Store(S);
  458.   AppStream.OleStream.lpstbl := @OleStreamVTbl;
  459.   AppStream.OwlStream := @S;
  460.   Check(OleSaveToStream(OleObject, @AppStream.OleStream));
  461. end;
  462.  
  463. { Paint an OLE object window. The contained OLE object is instructed to
  464.   draw itself to fill the entire client area. }
  465.  
  466. procedure TObjectWindow.Paint(PaintDC: HDC; var PaintInfo: TPaintStruct);
  467. var
  468.   R: TRect;
  469. begin
  470.   GetClientRect(HWindow, R);
  471.   Check(OleDraw(OleObject, PaintDC, R, R, 0));
  472. end;
  473.  
  474. { Determine whether an OLE object window can close. If the contained OLE
  475.   object is currently open, the user must confirm before the window can
  476.   be closed. }
  477.  
  478. function TObjectWindow.CanClose: Boolean;
  479. begin
  480.   CanClose := True;
  481.   if OleQueryOpen(OleObject) = ole_Ok then
  482.     CanClose := Message('Object is currently open. Continue anyway?',
  483.       mb_IconExclamation + mb_OkCancel) = id_Ok;
  484. end;
  485.  
  486. { Check the status of an OLE operation. If an OLE operation returns
  487.   ole_Wait_For_Release, indicating that it is executing acsynchronously,
  488.   the Check method will enter a message loop, waiting for the OLE object
  489.   to be released by the server. }
  490.  
  491. procedure TObjectWindow.Check(OleStatus: TOleStatus);
  492. var
  493.   M: TMsg;
  494. begin
  495.   if OleStatus = ole_Wait_For_Release then
  496.   begin
  497.     repeat
  498.       OleStatus := OleQueryReleaseStatus(OleObject);
  499.       if OleStatus = ole_Busy then
  500.         if GetMessage(M, 0, 0, 0) then
  501.         begin
  502.           TranslateMessage(M);
  503.           DispatchMessage(M);
  504.         end;
  505.     until OleStatus <> ole_Busy;
  506.   end;
  507.   if OleStatus <> ole_Ok then OleError(OleStatus);
  508. end;
  509.  
  510. { Return the class name of the contained OLE object. The first string in
  511.   an OLE object's ObjectLink or OwnerLink data is the class name. }
  512.  
  513. procedure TObjectWindow.GetObjectClass(ClassName: PChar);
  514. var
  515.   H: THandle;
  516. begin
  517.   ClassName[0] := #0;
  518.   if (OleGetData(OleObject, CFObjectLink, H) = ole_Ok) or
  519.     (OleGetData(OleObject, CFOwnerLink, H) = ole_Ok) then
  520.   begin
  521.     StrCopy(ClassName, GlobalLock(H));
  522.     GlobalUnlock(H);
  523.   end;
  524. end;
  525.  
  526. { Return True if the contained OLE object is a linked object. }
  527.  
  528. function TObjectWindow.IsLinked: Boolean;
  529. var
  530.   ObjectType: Longint;
  531. begin
  532.   IsLinked := (OleQueryType(OleObject, ObjectType) = ole_Ok) and
  533.     (ObjectType = ot_Link);
  534. end;
  535.  
  536. { Update the contained OLE object. }
  537.  
  538. procedure TObjectWindow.Update;
  539. begin
  540.   Check(OleUpdate(OleObject));
  541. end;
  542.  
  543. { Open the contained OLE object. }
  544.  
  545. procedure TObjectWindow.OpenObject(Verb: Word);
  546. begin
  547.   Check(OleActivate(OleObject, Verb, True, True, 0, nil));
  548. end;
  549.  
  550. { Close the contained OLE object if it is open. }
  551.  
  552. procedure TObjectWindow.CloseObject;
  553. begin
  554.   if OleQueryOpen(OleObject) = ole_Ok then Check(OleClose(OleObject));
  555. end;
  556.  
  557. { Copy the contained OLE object to the clipboard. }
  558.  
  559. procedure TObjectWindow.CopyToClipboard;
  560. begin
  561.   Check(OleCopyToClipboard(OleObject));
  562. end;
  563.  
  564. { Delete an OLE object window. If the window is the main window's
  565.   current selection, it is unselected. The parent window is marked as
  566.   modified, and the contained OLE object is closed and deleted. }
  567.  
  568. procedure TObjectWindow.Delete;
  569. begin
  570.   with MainWindow^ do
  571.   begin
  572.     if ObjectWindow = @Self then SelectWindow(nil);
  573.     Modified := True;
  574.   end;
  575.   CloseObject;
  576.   Check(OleDelete(OleObject));
  577.   OleObject := nil;
  578.   Free;
  579. end;
  580.  
  581. { This method is called by the ClientCallBack routine whenever the
  582.   contained OLE object has changed. The client area of the OLE object
  583.   window is invalidated to force repainting, and the main window is
  584.   marked as modified. }
  585.  
  586. procedure TObjectWindow.Changed;
  587. begin
  588.   InvalidateRect(HWindow, nil, True);
  589.   MainWindow^.Modified := True;
  590. end;
  591.  
  592. { Bring an OLE object window to front. }
  593.  
  594. procedure TObjectWindow.BringToFront;
  595. begin
  596.   SetWindowPos(HWindow, 0, 0, 0, 0, 0, swp_NoMove + swp_NoSize);
  597. end;
  598.  
  599. { Return the bounds of an OLE object window using parent window
  600.   coordinates. The bounds include the window frame, if present. }
  601.  
  602. procedure TObjectWindow.GetBounds(var R: TRect);
  603. begin
  604.   GetWindowRect(HWindow, R);
  605.   ScreenToClient(Parent^.HWindow, PPoint(@R.left)^);
  606.   ScreenToClient(Parent^.HWindow, PPoint(@R.right)^);
  607. end;
  608.  
  609. { Set the bounds of an OLE object window within its parent window. }
  610.  
  611. procedure TObjectWindow.SetBounds(var R: TRect);
  612. begin
  613.   MoveWindow(HWindow, R.left, R.top,
  614.     R.right - R.left, R.bottom - R.top, True);
  615.   UpdateWindow(HWindow);
  616. end;
  617.  
  618. { Enable or disable an OLE object window's window frame. The frame is
  619.   added or removed by modifying the window's style flags and growing or
  620.   shrinking the window's bounds. }
  621.  
  622. procedure TObjectWindow.ShowFrame(EnableFrame: Boolean);
  623. const
  624.   Border = ws_Border + ws_ThickFrame;
  625. var
  626.   FX, FY: Integer;
  627.   Style: Longint;
  628.   R: TRect;
  629. begin
  630.   if EnableFrame <> Framed then
  631.   begin
  632.     Style := GetWindowLong(HWindow, gwl_Style);
  633.     FX := GetSystemMetrics(sm_CXFrame);
  634.     FY := GetSystemMetrics(sm_CYFrame);
  635.     GetBounds(R);
  636.     if EnableFrame then
  637.     begin
  638.       Style := Style or Border;
  639.       InflateRect(R, FX, FY);
  640.     end else
  641.     begin
  642.       Style := Style and not Border;
  643.       InflateRect(R, -FX, -FY);
  644.     end;
  645.     SetWindowLong(HWindow, gwl_Style, Style);
  646.     SetBounds(R);
  647.     Framed := EnableFrame;
  648.   end;
  649. end;
  650.  
  651. { wm_GetMinMaxInfo message handler. Modifies the minimum window size. }
  652.  
  653. procedure TObjectWindow.WMGetMinMaxInfo(var Msg: TMessage);
  654. type
  655.   PMinMaxInfo = ^TMinMaxInfo;
  656.   TMinMaxInfo = array[0..4] of TPoint;
  657. begin
  658.   PMinMaxInfo(Msg.LParam)^[3].X := 24;
  659.   PMinMaxInfo(Msg.LParam)^[3].Y := 24;
  660. end;
  661.  
  662. { wm_Move message handler. Updates the window location in the Attr field
  663.   and marks the main window as modified. }
  664.  
  665. procedure TObjectWindow.WMMove(var Msg: TMessage);
  666. begin
  667.   if (Attr.X <> Msg.LParamLo) or (Attr.Y <> Msg.LParamHi) then
  668.   begin
  669.     Attr.X := Msg.LParamLo;
  670.     Attr.Y := Msg.LParamHi;
  671.     MainWindow^.Modified := True;
  672.   end;
  673. end;
  674.  
  675. { wm_Size message handler. Updates the window size in the Attr field and
  676.   marks the main window as modified. }
  677.  
  678. procedure TObjectWindow.WMSize(var Msg: TMessage);
  679. begin
  680.   if (Attr.W <> Msg.LParamLo) or (Attr.H <> Msg.LParamHi) then
  681.   begin
  682.     Attr.W := Msg.LParamLo;
  683.     Attr.H := Msg.LParamHi;
  684.     MainWindow^.Modified := True;
  685.   end;
  686. end;
  687.  
  688. { wm_LButtonDown message handler. Brings the window to front and selects
  689.   it, causing a frame to be drawn around the window. If a dragging
  690.   operation is not in effect, one is initiated by capturing the mouse
  691.   and recording the initial dragging location. }
  692.  
  693. procedure TObjectWindow.WMLButtonDown(var Msg: TMessage);
  694. begin
  695.   BringToFront;
  696.   MainWindow^.SelectWindow(@Self);
  697.   if not Dragging then
  698.   begin
  699.     Dragging := True;
  700.     SetCapture(HWindow);
  701.     DragPoint := TPoint(Msg.LParam);
  702.     ClientToScreen(HWindow, DragPoint);
  703.   end;
  704. end;
  705.  
  706. { wm_MouseMove message handler. If a dragging operation is in effect,
  707.   the window is moved and the client area of the parent window is
  708.   repainted. }
  709.  
  710. procedure TObjectWindow.WMMouseMove(var Msg: TMessage);
  711. var
  712.   P: TPoint;
  713.   R: TRect;
  714. begin
  715.   if Dragging then
  716.   begin
  717.     P := TPoint(Msg.LParam);
  718.     ClientToScreen(HWindow, P);
  719.     GetBounds(R);
  720.     OffsetRect(R, P.X - DragPoint.X, P.Y - DragPoint.Y);
  721.     SetBounds(R);
  722.     UpdateWindow(Parent^.HWindow);
  723.     DragPoint := P;
  724.   end;
  725. end;
  726.  
  727. { wm_LButtonUp message handler. Terminates a dragging operation. }
  728.  
  729. procedure TObjectWindow.WMLButtonUp(var Msg: TMessage);
  730. begin
  731.   if Dragging then
  732.   begin
  733.     ReleaseCapture;
  734.     Dragging := False;
  735.   end;
  736. end;
  737.  
  738. { wm_LButtonDblClk message handler. Opens the contained OLE object by
  739.   executing its primary verb. This is typically an 'Edit' or 'Play'
  740.   operation. }
  741.  
  742. procedure TObjectWindow.WMLButtonDblClk(var Msg: TMessage);
  743. begin
  744.   OpenObject(oleverb_Primary);
  745. end;
  746.  
  747. { TMainWindow methods }
  748.  
  749. { Construct the application's main window. Loads the main menu and
  750.   creates an OLE document. }
  751.  
  752. constructor TMainWindow.Init;
  753. var
  754.   P: PObjectWindow;
  755. begin
  756.   MainWindow := @Self;
  757.   TWindow.Init(nil, nil);
  758.   Attr.Menu := LoadMenu(HInstance, PChar(id_Menu));
  759.   ObjectWindow := nil;
  760.   SetFilename('');
  761.   InitDocument;
  762. end;
  763.  
  764. { Destroy the application's main window. Destroys the contained OLE
  765.   document. }
  766.  
  767. destructor TMainWindow.Done;
  768. begin
  769.   DoneDocument;
  770.   TWindow.Done;
  771. end;
  772.  
  773. { Determine whether the main window can close. Checks whether the
  774.   contained OLE object windows can close, and then prompts the user if
  775.   any modifications have been made since the file was opened or saved. }
  776.  
  777. function TMainWindow.CanClose: Boolean;
  778. begin
  779.   CanClose := False;
  780.   if TWindow.CanClose then
  781.   begin
  782.     CanClose := True;
  783.     if Modified then
  784.       case Message('Save current changes?',
  785.         mb_IconExclamation + mb_YesNoCancel) of
  786.         id_Yes: CanClose := Save;
  787.         id_Cancel: CanClose := False;
  788.       end;
  789.   end;
  790. end;
  791.  
  792. { Create the main window's OLE document. }
  793.  
  794. procedure TMainWindow.InitDocument;
  795. var
  796.   P: PChar;
  797. begin
  798.   P := Filename;
  799.   if P[0] = #0 then P := 'Untitled';
  800.   OleRegisterClientDoc('OleClntDemo', P, 0, ClientDoc);
  801.   Modified := False;
  802. end;
  803.  
  804. { Destroy the main window's OLE document. The contained OLE object
  805.   windows are destroyed before the document. }
  806.  
  807. procedure TMainWindow.DoneDocument;
  808.  
  809.   procedure FreeObjectWindow(P: PObjectWindow); far;
  810.   begin
  811.     P^.Free;
  812.   end;
  813.  
  814. begin
  815.   ForEach(@FreeObjectWindow);
  816.   OleRevokeClientDoc(ClientDoc);
  817. end;
  818.  
  819. { Update the main window's OLE document. Each object window is checked
  820.   to see if it contains a linked OLE object, and if so, the user is given
  821.   the option to update the link. }
  822.  
  823. procedure TMainWindow.UpdateDocument;
  824. var
  825.   Prompted, DoUpdate: Boolean;
  826.  
  827.   procedure UpdateObjectWindow(P: PObjectWindow); far;
  828.   begin
  829.     if P^.IsLinked then
  830.     begin
  831.       if not Prompted then
  832.       begin
  833.         DoUpdate := Message('This file contains linked objects.'#13 +
  834.           'Update links now?',
  835.           mb_IconExclamation + mb_YesNo) = id_Yes;
  836.         Prompted := True;
  837.       end;
  838.       if DoUpdate then P^.Update;
  839.     end;
  840.   end;
  841.  
  842. begin
  843.   Prompted := False;
  844.   ForEach(@UpdateObjectWindow);
  845. end;
  846.  
  847. { Set the name of the file in the main window. Updates the title of the
  848.   main window to include the base part of the filename. }
  849.  
  850. procedure TMainWindow.SetFilename(Name: PChar);
  851. var
  852.   Params: array[0..1] of PChar;
  853.   Title: array[0..63] of Char;
  854. begin
  855.   StrCopy(Filename, Name);
  856.   Params[0] := OleClntTitle;
  857.   if Name[0] = #0 then Params[1] := '(Untitled)' else
  858.   begin
  859.     Params[1] := StrRScan(Name, '\');
  860.     if Params[1] = nil then Params[1] := Name else Inc(Params[1]);
  861.   end;
  862.   wvsprintf(Title, '%s - %s', Params);
  863.   if hWindow <> 0 then SetCaption(Title);
  864. end;
  865.  
  866. { Load a file into the main window. If the file does not exist, a new
  867.   file is created. Otherwise, the file header is checked, and the
  868.   contained OLE object windows are read from the stream. }
  869.  
  870. function TMainWindow.LoadFile: Boolean;
  871. var
  872.   Header: TOleFileHeader;
  873.   S: TBufStream;
  874. begin
  875.   LoadFile := False;
  876.   S.Init(Filename, stOpenRead, 4096);
  877.   if S.Status = 0 then
  878.   begin
  879.     S.Read(Header, SizeOf(TOleFileHeader));
  880.     if Longint(Header) = Longint(OleFileHeader) then
  881.     begin
  882.       GetChildren(S);
  883.       if (S.Status = 0) and CreateChildren then
  884.       begin
  885.         UpdateDocument;
  886.         LoadFile := True;
  887.       end else
  888.         Error('Error reading file %s.', Filename);
  889.     end else
  890.       Error('File format error %s.', Filename);
  891.   end else
  892.     LoadFile := True;
  893.   S.Done;
  894. end;
  895.  
  896. { Save the file in the main window. The OLE client library is notified if
  897.   the file was successfully saved. }
  898.  
  899. function TMainWindow.SaveFile: Boolean;
  900. var
  901.   S: TBufStream;
  902. begin
  903.   SaveFile := False;
  904.   S.Init(Filename, stCreate, 4096);
  905.   if S.Status = 0 then
  906.   begin
  907.     S.Write(OleFileHeader, SizeOf(TOleFileHeader));
  908.     PutChildren(S);
  909.     if S.Status = 0 then
  910.     begin
  911.       OleSavedClientDoc(ClientDoc);
  912.       Modified := False;
  913.       SaveFile := True;
  914.     end else
  915.       Error('Error writing file %s.', Filename);
  916.   end else
  917.     Error('Error creating file %s.', Filename);
  918.   S.Done;
  919. end;
  920.  
  921. { Open a new or existing file. The current OLE document is destroyed, a
  922.   new document is created, and the file is loaded. }
  923.  
  924. function TMainWindow.NewFile(Name: PChar): Boolean;
  925. begin
  926.   DoneDocument;
  927.   SetFilename(Name);
  928.   InitDocument;
  929.   if Filename[0] <> #0 then NewFile := LoadFile else NewFile := True;
  930. end;
  931.  
  932. { Save the current file. If the file is untitled, prompt the user for a
  933.   name. }
  934.  
  935. function TMainWindow.Save: Boolean;
  936. begin
  937.   if Filename[0] = #0 then Save := SaveAs else Save := SaveFile;
  938. end;
  939.  
  940. { Save the current file under a new name. The OLE client library is
  941.   informed that the document has been renamed. }
  942.  
  943. function TMainWindow.SaveAs: Boolean;
  944. var
  945.   Name: TFilename;
  946. begin
  947.   SaveAs := False;
  948.   StrCopy(Name, Filename);
  949.   if FileDialog(HWindow, Name, True) then
  950.   begin
  951.     SetFilename(Name);
  952.     OleRenameClientDoc(ClientDoc, Name);
  953.     SaveAs := SaveFile;
  954.   end;
  955. end;
  956.  
  957. { Create a new OLE object window using data in the clipboard. The Link
  958.   parameter determines whether to create an embedded object or a linked
  959.   object. }
  960.  
  961. procedure TMainWindow.NewObjectWindow(Link: Boolean);
  962. begin
  963.   OpenClipboard(HWindow);
  964.   SelectWindow(PObjectWindow(Application^.MakeWindow(
  965.     New(PObjectWindow, Init(Link)))));
  966.   CloseClipboard;
  967. end;
  968.  
  969. { Select a given OLE object window. }
  970.  
  971. procedure TMainWindow.SelectWindow(Window: PObjectWindow);
  972. begin
  973.   if ObjectWindow <> Window then
  974.   begin
  975.     if ObjectWindow <> nil then ObjectWindow^.ShowFrame(False);
  976.     ObjectWindow := Window;
  977.     if ObjectWindow <> nil then ObjectWindow^.ShowFrame(True);
  978.   end;
  979. end;
  980.  
  981. { Update the Edit|Object menu. The Registration Database is queried to
  982.   find the readable version of the class name of the current OLE object,
  983.   along with the list of verbs supported by the class. If the class
  984.   supports more than one verb, the verbs are put on a popup submenu. }
  985.  
  986. procedure TMainWindow.UpdateObjectMenu;
  987. var
  988.   VerbFound: Boolean;
  989.   VerbCount: Word;
  990.   EditMenu, PopupMenu: HMenu;
  991.   Size: Longint;
  992.   Params: array[0..1] of Pointer;
  993.   ClassName, ClassText, Verb: array[0..31] of Char;
  994.   Buffer: array[0..255] of Char;
  995. begin
  996.   EditMenu := GetSubMenu(Attr.Menu, pos_Edit);
  997.   DeleteMenu(EditMenu, pos_Object, mf_ByPosition);
  998.   if ObjectWindow <> nil then
  999.   begin
  1000.     ObjectWindow^.GetObjectClass(ClassName);
  1001.     if ClassName[0] <> #0 then
  1002.     begin
  1003.       Size := SizeOf(ClassText);
  1004.       if RegQueryValue(hkey_Classes_Root, ClassName,
  1005.         ClassText, Size) = 0 then
  1006.       begin
  1007.         PopupMenu := CreatePopupMenu;
  1008.         VerbCount := 0;
  1009.         repeat
  1010.           Params[0] := @ClassName;
  1011.           Params[1] := Pointer(VerbCount);
  1012.           wvsprintf(Buffer, '%s\protocol\StdFileEditing\verb\%d', Params);
  1013.           Size := SizeOf(Verb);
  1014.           VerbFound := RegQueryValue(hkey_Classes_Root,
  1015.             Buffer, Verb, Size) = 0;
  1016.           if VerbFound then
  1017.           begin
  1018.             InsertMenu(PopupMenu, VerbCount, mf_ByPosition,
  1019.               cm_VerbMin + VerbCount, Verb);
  1020.             Inc(VerbCount);
  1021.           end;
  1022.         until not VerbFound;
  1023.         if VerbCount <= 1 then
  1024.         begin
  1025.           if VerbCount = 0 then
  1026.             Params[0] := PChar('Edit') else
  1027.             Params[0] := @Verb;
  1028.           Params[1] := @ClassText;
  1029.           wvsprintf(Buffer, '%s %s &Object', Params);
  1030.           InsertMenu(EditMenu, pos_Object, mf_ByPosition,
  1031.             cm_VerbMin, Buffer);
  1032.           DestroyMenu(PopupMenu);
  1033.         end else
  1034.         begin
  1035.           Params[0] := @ClassText;
  1036.           wvsprintf(Buffer, '%s &Object', Params);
  1037.           InsertMenu(EditMenu, pos_Object, mf_ByPosition + mf_Popup,
  1038.             PopupMenu, Buffer);
  1039.         end;
  1040.         Exit;
  1041.       end;
  1042.     end;
  1043.   end;
  1044.   InsertMenu(EditMenu, pos_Object, mf_ByPosition + mf_Grayed,
  1045.     0, '&Object');
  1046. end;
  1047.  
  1048. { wm_LButtonDown message handler. Deselects the current OLE object
  1049.   window. }
  1050.  
  1051. procedure TMainWindow.WMLButtonDown(var Msg: TMessage);
  1052. begin
  1053.   SelectWindow(nil);
  1054. end;
  1055.  
  1056. { wm_InitMenu message handler. Updates the Edit menu. }
  1057.  
  1058. procedure TMainWindow.WMInitMenu(var Msg: TMessage);
  1059. var
  1060.   HasSelection: Boolean;
  1061.  
  1062.   procedure SetMenuItem(Item: Word; Enable: Boolean);
  1063.   var
  1064.     Flags: Word;
  1065.   begin
  1066.     if Enable then Flags := mf_Enabled else Flags := mf_Grayed;
  1067.     EnableMenuItem(Attr.Menu, Item, Flags);
  1068.   end;
  1069.  
  1070. begin
  1071.   HasSelection := ObjectWindow <> nil;
  1072.   SetMenuItem(cm_EditCut, HasSelection);
  1073.   SetMenuItem(cm_EditCopy, HasSelection);
  1074.   SetMenuItem(cm_EditClear, HasSelection);
  1075.   SetMenuItem(cm_EditPaste, OleQueryCreateFromClip(
  1076.     OleProtocol, olerender_Draw, 0) = ole_OK);
  1077.   SetMenuItem(cm_EditPasteLink, OleQueryLinkFromClip(
  1078.     OleProtocol, olerender_Draw, 0) = ole_OK);
  1079.   UpdateObjectMenu;
  1080. end;
  1081.  
  1082. { File|New command handler. Checks whether the current file can be
  1083.   closed, and creates a new untitled file if possible. }
  1084.  
  1085. procedure TMainWindow.CMFileNew(var Msg: TMessage);
  1086. begin
  1087.   if CanClose then NewFile('');
  1088. end;
  1089.  
  1090. { File|Open command handler. Checks whether the current file can be
  1091.   closed, and opens a new file if possible. }
  1092.  
  1093. procedure TMainWindow.CMFileOpen(var Msg: TMessage);
  1094. var
  1095.   Name: TFilename;
  1096. begin
  1097.   if CanClose then
  1098.   begin
  1099.     Name[0] := #0;
  1100.     if FileDialog(HWindow, Name, False) then
  1101.       if not NewFile(Name) then NewFile('');
  1102.   end;
  1103. end;
  1104.  
  1105. { File|Save command handler. }
  1106.  
  1107. procedure TMainWindow.CMFileSave(var Msg: TMessage);
  1108. begin
  1109.   Save;
  1110. end;
  1111.  
  1112. { File|Save as command handler. }
  1113.  
  1114. procedure TMainWindow.CMFileSaveAs(var Msg: TMessage);
  1115. begin
  1116.   SaveAs;
  1117. end;
  1118.  
  1119. { File|Exit command handler. }
  1120.  
  1121. procedure TMainWindow.CMFileExit(var Msg: TMessage);
  1122. begin
  1123.   CloseWindow;
  1124. end;
  1125.  
  1126. { Edit|Cut command handler. Performs a Copy followed by a Clear. }
  1127.  
  1128. procedure TMainWindow.CMEditCut(var Msg: TMessage);
  1129. begin
  1130.   CMEditCopy(Msg);
  1131.   CMEditClear(Msg);
  1132. end;
  1133.  
  1134. { Edit|Copy command handler. If an OLE object window is currently
  1135.   selected, the clipboard is emptied, and the OLE object window is
  1136.   instructed to copy the contained OLE object to the clipboard. }
  1137.  
  1138. procedure TMainWindow.CMEditCopy(var Msg: TMessage);
  1139. begin
  1140.   if ObjectWindow <> nil then
  1141.   begin
  1142.     OpenClipBoard(HWindow);
  1143.     EmptyClipBoard;
  1144.     ObjectWindow^.CopyToClipboard;
  1145.     CloseClipBoard;
  1146.   end;
  1147. end;
  1148.  
  1149. { Edit|Paste command handler. Creates an embedded OLE object. }
  1150.  
  1151. procedure TMainWindow.CMEditPaste(var Msg: TMessage);
  1152. begin
  1153.   NewObjectWindow(False);
  1154. end;
  1155.  
  1156. { Edit|Paste link command handler. Creates a linked OLE object. }
  1157.  
  1158. procedure TMainWindow.CMEditPasteLink(var Msg: TMessage);
  1159. begin
  1160.   NewObjectWindow(True);
  1161. end;
  1162.  
  1163. { Edit|Clear command handler. Deletes the currently selected OLE object
  1164.   window, if possible. }
  1165.  
  1166. procedure TMainWindow.CMEditClear(var Msg: TMessage);
  1167. begin
  1168.   if ObjectWindow <> nil then
  1169.     if ObjectWindow^.CanClose then ObjectWindow^.Delete;
  1170. end;
  1171.  
  1172. { Help|About command handler. Brings up the About box. }
  1173.  
  1174. procedure TMainWindow.CMHelpAbout(var Msg: TMessage);
  1175. begin
  1176.   Application^.ExecDialog(New(PDialog, Init(@Self, PChar(id_About))));
  1177. end;
  1178.  
  1179. { Default command handler method. Called when no explicit command handler
  1180.   can be found. If the command is within the range reserved for OLE
  1181.   object verbs, the current OLE object window is instructed to execute
  1182.   the verb. }
  1183.  
  1184. procedure TMainWindow.DefCommandProc(var Msg: TMessage);
  1185. begin
  1186.   if (Msg.WParam >= cm_VerbMin) and (Msg.WParam <= cm_VerbMax) then
  1187.   begin
  1188.     if ObjectWindow <> nil then
  1189.       ObjectWindow^.OpenObject(Msg.WParam - cm_VerbMin);
  1190.   end else
  1191.     TWindow.DefCommandProc(Msg);
  1192. end;
  1193.  
  1194. { TApp methods }
  1195.  
  1196. { Construct the application object. Queries the pixels-per-inch ratios
  1197.   of the display for later use in conversions between mm_HiMetric and
  1198.   mm_Text coordinates. Creates callback procedure instances for the OLE
  1199.   client and OLE stream virtual tables. Registers the OwnerLink and
  1200.   ObjectLink clipboard formats for later use in OleGetData calls.
  1201.   Registers TObjectWindow for stream I/O. }
  1202.  
  1203. constructor TApp.Init(AName: PChar);
  1204. var
  1205.   DC: HDC;
  1206. begin
  1207.   TApplication.Init(AName);
  1208.   DC := GetDC(0);
  1209.   PixPerInch.X := GetDeviceCaps(DC, logPixelsX);
  1210.   PixPerInch.Y := GetDeviceCaps(DC, logPixelsY);
  1211.   ReleaseDC(0, DC);
  1212.   @OleClientVTbl.CallBack := MakeProcInstance(@ClientCallBack, HInstance);
  1213.   @OleStreamVTbl.Get := MakeProcInstance(@StreamGet, HInstance);
  1214.   @OleStreamVTbl.Put := MakeProcInstance(@StreamPut, HInstance);
  1215.   CFOwnerLink := RegisterClipboardFormat('OwnerLink');
  1216.   CFObjectLink := RegisterClipboardFormat('ObjectLink');
  1217.   RegisterType(RObjectWindow);
  1218. end;
  1219.  
  1220. { Destroy the application object. Frees the OLE client and OLE stream
  1221.   virtual table procedure instances. }
  1222.  
  1223. destructor TApp.Done;
  1224. begin
  1225.   FreeProcInstance(@OleClientVTbl.CallBack);
  1226.   FreeProcInstance(@OleStreamVTbl.Get);
  1227.   FreeProcInstance(@OleStreamVTbl.Put);
  1228.   TApplication.Done;
  1229. end;
  1230.  
  1231. { Create the main window. }
  1232.  
  1233. procedure TApp.InitMainWindow;
  1234. begin
  1235.   MainWindow := New(PMainWindow, Init);
  1236. end;
  1237.  
  1238. { Main program }
  1239.  
  1240. begin
  1241.   App.Init('OleClntDemo');
  1242.   App.Run;
  1243.   App.Done;
  1244. end.
  1245.